package sqlite_storage

import (
	"database/sql"
	"github.com/mattn/go-sqlite3"
	"go-iot-gateway/internal/apk/sqlites"
	"go-iot-gateway/internal/conf_table"
	"go-iot-gateway/internal/logger"
	storage_define "go-iot-gateway/internal/storage/define"
	"os"
	"strconv"
	"strings"
	"time"
	"xorm.io/xorm"
)

// 刷盘配置
type FlushConfig struct {
	//数据缓存大小
	FlushCacheSize int
	//最小的刷盘时间
	FlushIntervalMs int
	//一次最多写入多少条记录
	FlushSize int
}

// 创建最新值存储
func NewLatestStorage(url string, flushConfig FlushConfig) (storage_define.LatestStorage, error) {
	latestStorage := &SqliteLatestStorage{}
	latestStorage.init(flushConfig)

	path, err := sqlites.GetSqlitePath(url)
	if err != nil {
		return nil, err
	}
	//logger.Storage().Debug("sqlite实时库地址 " + path)

	err = createDbFile(path)
	if err != nil {
		return nil, err
	}

	database, err := sql.Open("sqlite3", path)
	if err != nil {
		logger.Storage().Error("无法打开 sqlite 文件", path)
		return nil, err
	}

	//初始化表,如果表不存在
	err = initLatestTable(database)
	if err != nil {
		logger.Storage().Error("初始化表失败", path)
		return nil, err
	}

	engine, err := xorm.NewEngine("sqlite3", path)

	if err != nil {
		return nil, err
	}
	latestStorage.dBEngine = engine

	//数据初始化
	err = initLatestData(latestStorage)
	if err != nil {
		return nil, err
	}

	//clearLatestData(latestStorage)

	return latestStorage, nil
}

// 创建数据库文件
func createDbFile(f string) error {
	_, err := os.Stat(f)
	if err == nil {
		return nil
	}

	//获取目录
	dirSplit := strings.Split(f, string(os.PathSeparator))
	dirSplit = dirSplit[:len(dirSplit)-1]
	dirs := strings.Join(dirSplit, string(os.PathSeparator))

	_ = os.MkdirAll(dirs, 0755)

	if os.IsNotExist(err) {
		_, err := os.Create(f)
		return err
	}
	return err
}

// 初始化表
func initLatestTable(database *sql.DB) error {

	exists, err := tablexEists(storage_define.LATEST_TABLE_NAME, database)
	if err != nil {
		return err
	}
	if exists {
		return nil
	}
	// 创建表
	sqlStmt := "CREATE TABLE IF NOT EXISTS " + storage_define.LATEST_TABLE_NAME + ` (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    tag_name TEXT NOT NULL unique,
					device_id  TEXT,
					num_val float,
  					str_val TEXT,
    				last_val  TEXT,
					ts int(15) not null,
					qos int(2) not null,
					is_fix int(2),
					channel_id TEXT,
					data_type TEXT,
    				updated_at int(15) not null
                );`
	_, err = database.Exec(sqlStmt)
	if err != nil {
		return err
	}
	return nil
}

// 判断表是否存在
func tablexEists(tableName string, database *sql.DB) (bool, error) {
	rows, err := database.Query("PRAGMA table_info('" + tableName + "');")
	if err != nil {
		if sqliteErr, ok := err.(sqlite3.Error); ok {
			if sqliteErr.Code == 1 { //no such table
				return true, nil
			}
		}
		return false, err
	}
	defer func() {
		err := rows.Close()
		if err != nil {
			logger.Storage().Warn("行关闭失败")
			return
		}
	}()

	err = rows.Err()
	if err != nil {
		return false, err
	}

	return rows.Next(), nil
}

// 初始化数据
func initLatestData(storage *SqliteLatestStorage) error {
	// 将全表的 qos 更新为 坏的
	_, err := storage.dBEngine.Exec("update " + storage_define.LATEST_TABLE_NAME + " set qos = " + strconv.Itoa((storage_define.QOS_BAD)))
	if err != nil {
		return err
	}

	for _, taginfo := range conf_table.FindTag() {
		if taginfo.IsSysReserve() { //系统保留
			continue
		}

		if !taginfo.DeviceInfo.IsValid() || !taginfo.DeviceInfo.ChannelInfo.IsValid() {
			continue
		}

		dataType := taginfo.DataTypeStr()
		numVal := float64(taginfo.Tag_init)
		if numVal == 0 {
			if strings.Contains(dataType, "float") {
				numVal = 0.0
			} else {
				numVal = 0
			}
		}

		latestVal := &LatestVal{
			TagName:   taginfo.Tag_name,
			DataType:  dataType,
			IsFix:     false,
			Qos:       storage_define.QOS_BAD,
			StrVal:    "",
			NumVal:    numVal,
			LastVal:   "",
			Ts:        time.Now().UnixMilli(),
			ChannelId: taginfo.DeviceInfo.ChannelInfo.Chl_id,
			DeviceId:  taginfo.DeviceInfo.Dev_id,
			UpdatedAt: time.Now().UnixMilli(),
		}

		if storage.ExistsLatestVal(taginfo.Tag_name) {
			_, err2 := storage.updateSchemaByTagName(latestVal)
			if err2 != nil {
				logger.Storage().Error("修改元数据失败 error ", err2)
			}
			continue
		}

		r, err := storage.createVal(latestVal)
		if err != nil || r == 0 {
			logger.Storage().Error("插入数据失败 error ", err, " result ", r)
			panic("实时库初始化失败 ")
		}

	}

	//todo 删除多余的数据??
	//todo 从库里查询所有记录，此后删除多余的数据

	return nil
}
